06. Scale the Star

L3 05 Scale

Scale the Star

Now you’re going to fill in the body of the scaler() function. This time, you’re going to animate two properties in parallel.

In the previous two steps, you were just animating one property, because that’s all that was needed: rotating around a single axis (the “z” axis, which runs perpendicular to the screen) and translating along a single axis (the “x” axis, which runs left to right on the screen).

But when an object is scaled, it is usually scaled in x and y simultaneously, to avoid making it look “stretched” along one of the axes (“fun-house mirror” is usually not the effect we strive for in UI design). So you should create an animation that will animate both the SCALE_X and SCALE_Y properties at the same time.

Note: There is no single property that scales in both the x and y dimensions, so animations that scale in both x and y need to animate both of these separate properties in parallel.

There are a couple of ways to do this (including using an AnimatorSet, which you will see in the final step of this lab). But a good technique to know about uses PropertyValuesHolder, which is an object that holds information about both a property and the values that that property should animate between.

Note: A PropertyValuesHolder holds information about a single property, along the with the values that that property animates between. An ObjectAnimator can hold multiple PropertyValuesHolder objects, which will all animate together, in parallel, when the ObjectAnimator starts. The target that these PropertyValueHolder objects animate is specified by the ObjectAnimator. The ideal use case for ObjectAnimators which use PropertyValuesHolder parameters is when you need to animate several properties on the same object in parallel.

Step 1: Create an animation using PropertyValuesHolder

In the previous tasks, you supplied information to ObjectAnimator about the property to be animated (such as TRANSLATE_X) along with the animation values (for example, the end value of 200f for translation). But you can instead use an intermediate object called PropertyValuesHolder to hold this information, and then create a single ObjectAnimator with multiple PropertyValuesHolder objects. This single animator will then run an animation on two or more of these sets of properties/values together.

  1. First, create two PropertyValuesHolder objects as the first lines in scaler():
val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)

Scaling to a value of 4f means the star will scale to 4 times its default size.

Note: These PropertyValuesHolder objects look similar to the ObjectAnimators you created previously, but they only hold the property and value information for the animation, not the target. So even if you wanted to run these as an animation, you could not; there’s not enough information because you haven’t told the system which target object(s) to animate.

  1. Now create an ObjectAnimator object, as before, but use the scaleX and scaleY objects you created above to specify the property/value information:
val animator = ObjectAnimator.ofPropertyValuesHolder(star, scaleX, scaleY)

This is similar to the animators you created previously, but instead of defining a property and a set of values, it uses multiple PropertyValuesHolders which contain all of that information already. Using several PropertyValuesHolder objects in a single animator will cause them all to be animated in parallel.

Step 2: Cleaning up the animation

Now you can complete the method as you did in previous tasks, resetting the object back to a reasonable end state and avoiding problems with discontinuous animations.

  1. As with the translater() function, you want to make this a repeating/reversing animation to leave the star’s SCALE_X and SCALE_Y properties at their default values (1.0) when the animation is done. Do this by setting the appropriate repeatCount and repeatMode values on the animator:
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
  1. Also, as with the previous animations, call the disableViewDurationAnimation() extension function to disable scaleButton during the animation. Adding in the rest of this code results in the final version of the function:
private fun scaler() {
    val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
    val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)
    val animator = ObjectAnimator.ofPropertyValuesHolder(
            star, scaleX, scaleY)
    animator.repeatCount = 1
    animator.repeatMode = ObjectAnimator.REVERSE
    animator.disableViewDuringAnimation(scaleButton)
    animator.start()
}
  1. Run the application. Note that the star now scales out to 4x its original size… and then returns to its original state.